<p>
  The implementation of this algorithm uses the <a href="https://www.quantconnect.com/docs/algorithm-framework/overview">alpha framework</a>.
  The algorithm picks 35 country indexes ETFs as the trading universe. As the symbols in the universe don't change over time,
  we use the <code>ManualUniverseSelectionModel</code> to subscribe the daily data for those symbols.
</p>
<p>
  Beta is a statistical measure of a stock's volatility in relation to the market.
  Stock analysts use this measure to get a sense of stocks' risk profiles.
  The formula for calculating beta is the covariance of the return of an asset with the return of the market divided by the variance of the return of the market over a certain period.
</p>
\[\beta_i=\frac{cov(R_i,R_{m})}{Var(R_m)}\]
<div class="section-example-container">
<pre class="python">
def beta(self, asset_return, market_return):
    asset_return = np.array(asset_return, dtype=np.float32)
    market_return = np.array(market_return, dtype=np.float32)
    return np.cov(asset_return, market_return)[0][1]/np.var(market_return)
</pre>
</div>
<p>
  We use S&amp;P500 ETF as the market measure. The beta for each country is calculated with respect to the SPY using a 1-year rolling window.
  <code>SymbolData</code> class save the one-year rolling window price data.
</p>
<div class="section-example-container">
<pre class="python">
class SymbolData:
    def __init__(self, symbol):
        self.Symbol = symbol
        self.Price = deque(maxlen=253)
</pre>
</div>
<p>
  <code>self.assets</code> is a dictionary to save the price series for each country ETF. The key is the ETF symbol.
  For new symbols added to the algorithm, we request the one-year history data to initialize the price series.
</p>
<div class="section-example-container">
<pre class="python">
def OnSecuritiesChanged(self, algorithm, changes):
    for added in changes.AddedSecurities:
        if added.Symbol.Value == "SPY":
            self.market_price = deque(maxlen=253)
            hist_SPY = algorithm.History(["SPY"], 500, Resolution.Daily)
            for i in hist_SPY.loc["SPY"].itertuples():
                self.market_price.append(i.close)

        if added not in self.assets and added.Symbol.Value != "SPY":
            hist = algorithm.History([added.Symbol.Value], 500, Resolution.Daily)
            if not hist.empty:
                self.assets[added.Symbol] = SymbolData(added)
                for i in hist.loc[added.Symbol.Value].itertuples():
                    self.assets[added.Symbol].Price.append(i.close)

    for removed in changes.RemovedSecurities:
        self.assets.pop(removed.Symbol)
</pre>
</div>
<p>
  In the Alpha model, <code>Update(self, algorithm, data)</code> method updates this model with the latest data from the algorithm.
  This method is called each time the algorithm receives data for subscribed securities. In this method, we update the price series with new trade bars.
  The price series is converted to return series. We plug the market return and the country ETF return into the beta formula.
  ETFs are then ranked in ascending order by their estimated beta. The ranked ETFs are assigned to one of two portfolios: low beta and high beta.
  Each portfolio contains a quarter of the total assets.
</p>
<div class="section-example-container">
<pre class="python">
def Update(self, algorithm, data):
    if data.ContainsKey("SPY"):
        self.market_price.append(float(algorithm.Securities["SPY"].Price))
    for key, value in self.assets.items():
        if data.ContainsKey(key):
            value.Price.append(float(algorithm.Securities[key].Price))
    insights = []
    if self.month != algorithm.Time.month:
        self.month = algorithm.Time.month
        beta_values = {}
        market_return = np.diff(np.array(self.market_price))/np.array(self.market_price)[:-1]
        long = None
        for key, value in self.assets.items():
            if key != "SPY" and len(value.Price) == value.Price.maxlen:
                asset_return = np.diff(np.array(value.Price))/np.array(value.Price)[:-1]
                beta_values[key] = self.beta(asset_return, market_return)
        sorted_by_beta = sorted(beta_values, key = lambda x: beta_values[x])
</pre>
</div>
<p>
  The algorithm shorts the high-beta portfolio and longs the low-beta portfolio. Securities are rebalanced every calendar month.
  <code>PortfolioConstructionModel</code> is set to emit the target weight monthly.
  The time period of insight is from the current day to the end of the calendar month.
  In Python calendar library, <code>calendar.monthrange(year, month)</code> returns the number of days in a month for the specified year and month.
  The insight direction is set to be flat for symbols removed from the long/short list at the end of the month.
</p>

<div class="section-example-container">
<pre class="python">
long = sorted_by_beta[:int(0.25*len(sorted_by_beta))]
short = sorted_by_beta[-int(0.25*len(sorted_by_beta)):]
# day: the weekday of first day of the month
# num_days: number of days in month
day, num_days = calendar.monthrange(algorithm.Time.year, algorithm.Time.month)
insight_period = num_days - algorithm.Time.day - 1
if long and short:
    invested = [x.Key for x in algorithm.Portfolio if x.Value.Invested]
    for i in invested:
        if algorithm.Portfolio[i].IsLong and i not in long:
            insights.append(Insight.Price(i, timedelta(days=1), InsightDirection.Flat))
        if algorithm.Portfolio[i].IsShort and i not in short:
            insights.append(Insight.Price(i, timedelta(days=1), InsightDirection.Flat))
    for i in long:
        insights.append(Insight.Price(i, timedelta(days=insight_period), InsightDirection.Up))
    for i in short:
        insights.append(Insight.Price(i, timedelta(days=insight_period), InsightDirection.Down))
</pre>
</div>
